interface Option{
    el:any,
    data:any
}



class Mvvm{
    $el:any;
    $data:any;
    constructor(option:Option){
        this.$el = option.el;
        this.$data = option.data;

        if(this.$el){
            //数据劫持
            new Observer<any>(this.$data);

            //模板编译
            new Compile(this.$el,this);
        }
    }
}

class Dep{
    watchers:any;
    static target:any;
    constructor(){
        //存放观察者
        this.watchers = []
    }

    add(watcher:any){
        this.watchers.push(watcher);
    }

    notify(){
        this.watchers.forEach((item:any) => {
            item.update()
        })
    }
}

//观察者
class Watcher{
    vm:any;
    expr:string;
    cb:any;
    oldVal:any;
    constructor(vm:any,expr:string,cb:any){
        this.vm = vm;
        this.expr = expr;
        this.cb = cb;
        //获取旧值
        this.oldVal = this.getVal();
    }

    getVal(){
        Dep.target = this;
        let value = compileUtil.getVal(this.expr,this.vm);
        Dep.target = null;
        return value
    }
    //更新函数
    update(){
        let newVal = compileUtil.getVal(this.expr,this.vm);
        if(newVal != this.oldVal){
            this.cb(newVal);
        }
    }
}

//vm.$watch(vm,'user.username',(newVal) => {})


//数据劫持

interface ObserverInter<T>{
    observer(data:T): void;
    observerData(data:T,key:string,value:any):void
}

class Observer<T> implements ObserverInter<T>{
    constructor(data:T){
        this.observer(data);
    }

    //数据劫持
    observer(data:T){
        if(data && typeof data === 'object'){
            for(let key in data){
                this.observerData(data,key,data[key]);
            }
        }
    }

    observerData(data:T,key:string,value:any){
        this.observer(value);
        let dep = new Dep();
        Object.defineProperty(data,key,{
            set(newValue:any){
                if(newValue != value){
                    value = newValue;
                    //通知数据变化
                    dep.notify();
                }
            },
            get(){
                Dep.target && dep.add(Dep.target);
                return value
            }
        })
    }
}


//编译模板和数据
class Compile {
    vm:any;
    el:any;
    constructor(vm:any,el:any){
        this.vm = vm;
        this.el = this.isElementNode(el) ? el : document.querySelector(el);
        //编译 把根节点所有的子节点放到文档片段里
        let fragment = this.nodeToFragment(this.el);
        console.log(fragment)

        //文档片段和数据进行编译，初始化视图

        this.compile(fragment);

        this.el.appendChild(fragment);
    }

    //文档片段和数据进行编译，初始化视图

    compile(node:any){
        let childNodes = node.childNodes;

        //区分是元素节点还是文本节点
        [...childNodes].forEach(item => {
            if(this.isElementNode(item)){
                //元素节点
                this.compileElement(item);
            }else{
                //文本节点
                this.compileText(item);
            }
        })

    }

    isDirective(attr:string){
        // console.log('=====','attr'.startsWith(0,1))
        return attr.substr(0,2) === 'v-'
    }

    //编译元素节点

    compileElement(node:any){
        let attrs = node.attributes;
        console.dir(attrs);
        [...attrs].forEach((item:any) => {
            //区分是否是指令属性 type v-model
            
            let {name,value:expr} = item;
            console.log("====",name)
            if(this.isDirective(name)){
                let [,directive] = name.split('-');
                //从vm.$data上查找数据，并且替换表达式
                compileUtil[directive](node,expr,this.vm)
            }
        })
    }

    //编译文本节点
    compileText(node:any){
        let content = node.textContent;
        console.log(content);
        //{{}}
        if(/\{\{(.+?)\}\}/.test(content)){
            compileUtil.content(node,content,this.vm);
        }
    }

    nodeToFragment(node:any){
        //创建文档片段
        let frament = document.createDocumentFragment();

        let childnodes = node.childNodes;

        [...childnodes].forEach(item => {
            frament.appendChild(item);
        })

        return frament
    }

    isElementNode(node:any):boolean{
        return node.nodeType === 1
    }
}

let compileUtil:any = {
    //根据表达式从vm.$data上获取值
    getVal(expr:string,vm:any){
        //user.name
        return expr.split('.').reduce((pre:any,cur:string) => {
            return pre[cur]
        },vm.$data)
    },
    //设置值
    setVal(expr:string,vm:any,value:any){
        //[user,name]
        expr.split('.').reduce((pre:any,cur:string,index:number,arr:any[]) => {
            if(index === arr.length-1){
                pre[cur] = value
            }
            return pre[cur]
        },vm.$data)
    },
    model(node:any,expr:string,vm:any){
        let val = this.getVal(expr,vm);
        let fn = this.updater.compileModel;
        fn(node,val);
        //生成观察者
        new Watcher(vm,expr,(newVal:any) => {
            fn(node,newVal);
        })
        
        //添加input事件
        node.addEventListener('input',(e:any) => {
            //vm.$data expr  e.target.value
            this.setVal(expr,vm,e.target.value)
        })
    },
    content(node:any,content:string,vm:any){
        let val = content.replace(/\{\{(.+?)\}\}/g,(...argv:string[]) => {
            console.log(argv)
            new Watcher(vm,argv[1],(newVal:any) => {
                fn(node,newVal);
            })
            return this.getVal(argv[1],vm)
        })
        let fn = this.updater.compileText;
        fn(node,val);

    },
    updater:{
        compileModel(node:any,value:any){
            node.value = value;
        },
        compileText(node:any,value:any){
            node.textContent = value
        }
    }
}